package environnement;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import robot.IRobot;
import environnement.objets.IObjet;
import graphique.IDessinable;

/**
 * Class Environnement
 * Environnement global
 */
public class Environnement implements IDessinable
{

    /**
     * Couleur des bords
     */
    private int color_bords;
    /**
     * Couleur du fond
     */
    private int color_bg;
    /**
     * absisse
     */
    private int x = 5;
    /**
     * ordonné
     */
    private int y = 25;
    //
    // Fields
    //
    /**
     * Largeur de l'environnement
     */
    private int largeur;
    /**
     * Longueur de l'environnement
     */
    private int longueur;
    /**
     * Liste des objets présent dans l'environnement
     */
    private ArrayList<IObjet> liste_objets;
    /**
     * Liste des groupes de robots
     */
    private ArrayList<ArrayList<IRobot>> listes_Robots;
    /**
     * Instance static
     */
    private static Environnement instance;

    //
    // Constructors
    //

    /**
     * Constructeur redéfini comme étant privé pour interdire
     * son appel et forcer à passer par la méthode 
     */
    private Environnement()
    {
        this.color_bords = Color.GRAY.getRGB();
        this.color_bg = Color.WHITE.getRGB();
        this.largeur = 500;
        this.longueur = 400;
    }

//    /**
//     * Constructeur redéfini comme étant privé pour interdire
//     * son appel et forcer à passer par la méthode.
//     * @param color_bords
//     * @param color_bg
//     * @param largeur
//     * @param longueur
//     * @param liste_objets
//     * @param listes_Robots
//     */
//     Environnement(int color_bords, int color_bg, int largeur, int longueur,
//            ArrayList<IObjet> liste_objets,
//            ArrayList<ArrayList<IRobot>> listes_Robots)
//    {
//        super();
//        this.color_bords = color_bords;
//        this.color_bg = color_bg;
//        this.largeur = largeur;
//        this.longueur = longueur;
//        this.liste_objets = liste_objets;
//        this.listes_Robots = listes_Robots;
//    }

    /**
     * Récupère l'instance unique de la class Singleton.
     * Remarque : le constructeur est rendu inaccessible
     * @return
     */
    public static Environnement getInstance()
    {
        if (null == instance)
        { // Premier appel
            instance = new Environnement();
        }
        return instance;
    }

    //
    // Methods
    //
    //
    // Accessor methods
    //
    /**
     * Set the value of largeur
     * Largeur de l'environnement
     * @param newVar the new value of largeur
     */
    public void setLargeur(int newVar)
    {
        largeur = newVar;
    }

    /**
     * Get the value of largeur
     * Largeur de l'environnement
     * @return the value of largeur
     */
    public int getLargeur()
    {
        return largeur;
    }

    /**
     * Set the value of longueur
     * Longueur de l'environnement
     * @param newVar the new value of longueur
     */
    public void setLongueur(int newVar)
    {
        longueur = newVar;
    }

    /**
     * Get the value of longueur
     * Longueur de l'environnement
     * @return the value of longueur
     */
    public int getLongueur()
    {
        return longueur;
    }

    /**
     * Set the value of liste_objets
     * Liste des objets présent dans l'environnement
     * @param newVar the new value of liste_objets
     */
    public void setListe_objets(ArrayList<IObjet> newVar)
    {
        liste_objets = newVar;
    }

    /**
     * Get the value of liste_objets
     * Liste des objets présent dans l'environnement
     * @return the value of liste_objets
     */
    public ArrayList<IObjet> getListe_objets()
    {
        return liste_objets;
    }

    /**
     * Set the value of listes_Robots
     * Liste des groupes de robots
     * @param newVar the new value of listes_Robots
     */
    public void setListes_Robots(ArrayList<ArrayList<IRobot>> newVar)
    {
        listes_Robots = newVar;
    }

    /**
     * Get the value of listes_Robots
     * Liste des groupes de robots
     * @return the value of listes_Robots
     */
    public ArrayList<ArrayList<IRobot>> getListes_Robots()
    {
        return listes_Robots;
    }

    /**
     * Retourne l'absisse du rectangle représentant l'environnement
     * @return
     */
    public int getX()
    {
        return x;
    }

    /**
     * Retourne l'ordonnée du rectangle représentant l'environnement
     * @return
     */
    public int getY()
    {
        return y;
    }

    //
    // Other methods
    //
    /**
     * Modifie l'état de l'environnement suivant les actions des robots
     */
    public void etapeSuivante()
    {
        for (int groupe = 0; groupe < this.listes_Robots.size(); ++groupe)
        {
            for (int robot = 0; robot < this.listes_Robots.get(groupe).size(); ++robot)
            {
                this.listes_Robots.get(groupe).get(robot).executeActions(this.listes_Robots.get(groupe).get(robot));
            }
            for (int robot = 0; robot < this.listes_Robots.get(groupe).size(); ++robot)
            {
                this.listes_Robots.get(groupe).get(robot).updateCapteurs(this.listes_Robots.get(groupe).get(robot));
            }
            for (int robot = 0; robot < this.listes_Robots.get(groupe).size(); ++robot)
            {
                this.listes_Robots.get(groupe).get(robot).updateInfos(this.listes_Robots.get(groupe).get(robot));
            }
        }
    }

    /**
     * Dessiner l'environnement
     */
    public void dessiner(Graphics2D gr)
    {
        //on efface le tour precedent

        //On dessine l'environnement
        Rectangle2D.Double environnement = new Rectangle2D.Double(this.x, this.y, this.largeur, this.longueur);
        gr.setPaint(new Color(this.color_bg));
        gr.fill(environnement);
        gr.setPaint(new Color(this.color_bords));
        gr.draw(environnement);


        //On dessine les objets
        for (int objs = 0; objs < this.liste_objets.size(); ++objs)
        {
            this.liste_objets.get(objs).dessiner(gr);
        }

        //On dessine les robots
        //Pour tous les groups de robots
        for (int groupe = 0; groupe < this.listes_Robots.size(); ++groupe)
        {
            //pour tous les robots
            for (int robot = 0; robot < this.listes_Robots.get(groupe).size(); ++robot)
            {
                this.listes_Robots.get(groupe).get(robot).dessiner(gr);
            }
        }


    }

    /**
     * Permet de savoir si un point est dans l'environnement
     * @param x
     * @param y
     * @return Vrai quand dehors sinon faux
     */
    public boolean isIn(int x, int y)
    {
        boolean bRetour = false;

        if (x > 0 && x < this.largeur && y > 0 && y < this.longueur)
        {
            bRetour = false;
        }
        else
        {
            bRetour = true;
        }

        return bRetour;
    }

    /**
     * Teste si la futur position d'un robot se trouve dans un obstacle de type
     * Objet, Robot (autre que lui meme) ou bien en dehors de l'environement
     * @param x
     * @param y
     * @return Retourne l'objet, robot ou environnement lorsque celui ci en dedans pour l'objet et
     * en dehors pour l'environnement
     */
    public IDessinable isInSmth(int x, int y)
    {
        // On regarde si on ne se trouve pas endehors de l'environnement
        if (this.isIn(x, y))
        {
            return this;
        }

        // On parcourt tous les objets
        for (IDessinable Objet : this.liste_objets)
        {
            // Si on est dans un Objet
            if (Objet.isIn(x, y))
            {
                return Objet;
            }
        }

        // On parcourt les groupes des Robot
        for (ArrayList<IRobot> GroupeRobot : this.listes_Robots)
        {
            // On parcourt les robots
            for (IDessinable Robot : GroupeRobot)
            {
                // Si on est dans un Objet
                if (Robot.isIn(x, y))
                {
                    return Robot;
                }
            }
        }

        return null;
    }
}
